package com.cundong.izhihu.fragment; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.PullToRefreshLayout; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.content.res.TypedArray; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.JsonReader; import android.util.JsonToken; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.JavascriptInterface; import android.webkit.JsResult; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; import com.cundong.izhihu.Constants; import com.cundong.izhihu.R; import com.cundong.izhihu.ZhihuApplication; import com.cundong.izhihu.activity.NewsDetailImageActivity; import com.cundong.izhihu.entity.NewsDetailEntity; import com.cundong.izhihu.task.DetailImageDownloadTask; import com.cundong.izhihu.task.GetNewsDetailTask; import com.cundong.izhihu.task.MyAsyncTask; import com.cundong.izhihu.task.ResponseListener; import com.cundong.izhihu.util.AssetsUtils; import com.cundong.izhihu.util.GsonUtils; import com.cundong.izhihu.util.NetWorkHelper; import com.cundong.izhihu.util.ZhihuUtils; /** * 类说明: 新闻详情页Fragment * * @date 2014-9-20 * @version 1.0 */ public class NewsDetailFragment extends BaseFragment implements ResponseListener { private ProgressBar mProgressBar; private WebView mWebView; private long mNewsId = 0; private NewsDetailEntity mNewsDetailEntity = null; private ArrayList<String> mDetailImageList = new ArrayList<String>(); private OnContentLoadListener mListener = null; @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnContentLoadListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnContentLoadListener"); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new LoadCacheDetailTask().executeOnExecutor( MyAsyncTask.THREAD_POOL_EXECUTOR, String.valueOf(mNewsId)); new GetNewsDetailTask(getActivity(), this).executeOnExecutor( MyAsyncTask.THREAD_POOL_EXECUTOR, String.valueOf(mNewsId)); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_detail, container, false); mProgressBar = (ProgressBar) rootView.findViewById(R.id.progress); mPullToRefreshLayout = (PullToRefreshLayout) rootView.findViewById(R.id.ptr_layout); mWebView = (WebView) rootView.findViewById(R.id.webview); setUpWebViewDefaults(mWebView); mWebView.setWebViewClient(mWebViewClient); return rootView; } @SuppressLint({ "SetJavaScriptEnabled", "NewApi" }) private void setUpWebViewDefaults(WebView webView) { mWebView.addJavascriptInterface(new JavaScriptObject(getActivity()), "injectedObject"); // 设置缓存模式 mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); mWebView.getSettings().setJavaScriptEnabled(true); // Use WideViewport and Zoom out if there is no viewport defined mWebView.getSettings().setUseWideViewPort(true); mWebView.getSettings().setLoadWithOverviewMode(true); mWebView.setVerticalScrollBarEnabled(false); mWebView.setHorizontalScrollBarEnabled(false); // 支持通过js打开新的窗口 mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); mWebView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { //Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show(); result.cancel(); return true; } @Override public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { return true; } }); //设置webView背景 Resources.Theme theme = getActivity().getTheme(); TypedArray typedArray = null; SharedPreferences mPerferences = PreferenceManager .getDefaultSharedPreferences(getActivity()); if (mPerferences.getBoolean("dark_theme?", false)) { typedArray = theme.obtainStyledAttributes(R.style.Theme_Daily_AppTheme_Dark, new int[] { R.attr.webViewBackground }); } else { typedArray = theme.obtainStyledAttributes(R.style.Theme_Daily_AppTheme_Light, new int[] { R.attr.webViewBackground }); } mWebView.setBackgroundColor(this.getResources().getColor(typedArray.getResourceId(0, 0))); if (Build.VERSION.SDK_INT >= 11) { mWebView.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null); } } private void setWebViewShown(boolean shown) { mWebView.setVisibility(shown ? View.VISIBLE : View.GONE); mProgressBar.setVisibility(shown ? View.GONE : View.VISIBLE); } @Override protected void doRefresh() { if (isAdded()) { new GetNewsDetailTask(getActivity(), this).executeOnExecutor(MyAsyncTask.THREAD_POOL_EXECUTOR, String.valueOf(mNewsId)); } } @Override public void onPreExecute() { } @Override public void onPostExecute(String content) { if (isAdded()) { // Notify PullToRefreshLayout that the refresh has finished mPullToRefreshLayout.setRefreshComplete(); if (!TextUtils.isEmpty(content)) { setWebView(content, true); } } setWebViewShown(true); } @Override public void onFail(final Exception e) { setWebViewShown(true); dealException(e); } /** * 设置WebView内容 * * @param content * @param isUpdateMode 是否为刷新操作 */ private void setWebView(String content, boolean isUpdateMode) { if (!isAdded()) { return; } if (isUpdateMode) { if (TextUtils.isEmpty(content)) { return; } } mNewsDetailEntity = (NewsDetailEntity) GsonUtils.getEntity( content, NewsDetailEntity.class); if (mNewsDetailEntity == null || TextUtils.isEmpty(mNewsDetailEntity.body)) { return; } //tell the activity, mNewsDetailEntity is okey mListener.onComplete(mNewsDetailEntity); String html = AssetsUtils.loadText(getActivity(), Constants.TEMPLATE_DEF_URL); html = html.replace("{content}", mNewsDetailEntity.body); //是否夜间模式 SharedPreferences mPerferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); html = html.replace("{nightTheme}", mPerferences.getBoolean("dark_theme?", false) ? "true" : "false"); String headerDef = "file:///android_asset/www/news_detail_header_def.jpg"; if (NetWorkHelper.isMobile(getActivity()) && PreferenceManager.getDefaultSharedPreferences( getActivity()).getBoolean("noimage_nowifi?", false) ) { } else { headerDef = mNewsDetailEntity.image; } StringBuilder sb = new StringBuilder(); sb.append("<div class=\"img-wrap\">") .append("<h1 class=\"headline-title\">") .append(mNewsDetailEntity.title).append("</h1>") .append("<span class=\"img-source\">") .append(mNewsDetailEntity.image_source).append("</span>") .append("<img src=\"").append(headerDef) .append("\" alt=\"\">") .append("<div class=\"img-mask\"></div>"); html = html.replace("<div class=\"img-place-holder\">", sb.toString()); String resultHTML = replaceImgTagFromHTML(html); mWebView.loadDataWithBaseURL(null, resultHTML, "text/html", "UTF-8", null); } /** * 替换html中的<img标签的属性 * * @param html * @return */ private String replaceImgTagFromHTML(String html) { Document doc = Jsoup.parse(html); Elements es = doc.getElementsByTag("img"); for (Element e : es) { String imgUrl = e.attr("src"); mDetailImageList.add(imgUrl); String localImgPath = ZhihuUtils.getCacheImgFilePath(getActivity(), imgUrl); e.attr("src_link", "file://" + localImgPath); e.attr("ori_link", imgUrl); if(!imgUrl.equals(mDetailImageList.get(0))) { e.attr("src", ""); } if (!imgUrl.equals(mDetailImageList.get(0)) && !e.attr("class").equals("avatar") ) { e.attr("onclick", "openImage('" + localImgPath + "')"); } } return doc.html(); } private class LoadCacheDetailTask extends MyAsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { String key = "detail_" + params[0]; return ZhihuApplication.getDataSource().getContent(key); } @Override protected void onPostExecute(String result) { super.onPostExecute(result); if (isAdded()) { if (!TextUtils.isEmpty(result)) { setWebViewShown(true); setWebView(result, false); } else { setWebViewShown(false); } } } } @SuppressLint("NewApi") private WebViewClient mWebViewClient = new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { Intent intent = new Intent("android.intent.action.VIEW", Uri.parse(url)); startActivity(intent); return true; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); mLogger.i("onPageFinished : " + url); String urlStrArray[] = new String[mDetailImageList.size()]; mDetailImageList.toArray(urlStrArray); if( !isAdded() ) { return; } if (NetWorkHelper.isMobile(getActivity()) && PreferenceManager.getDefaultSharedPreferences( getActivity()).getBoolean("noimage_nowifi?", false) ) { // 无图模式 } else { new DetailImageDownloadTask(getActivity(), new ResponseListener() { @Override public void onPreExecute() { } @Override public void onPostExecute(String content) { if (!isAdded()) { return; } String javascript = "img_replace_all();"; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // In KitKat+ you should use the evaluateJavascript method mWebView.evaluateJavascript(javascript, new ValueCallback<String>() { @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onReceiveValue(String s) { JsonReader reader = new JsonReader(new StringReader(s)); // Must set lenient to parse single values reader.setLenient(true); try { if(reader.peek() != JsonToken.NULL) { if(reader.peek() == JsonToken.STRING) { String msg = reader.nextString(); if(msg != null) { // Toast.makeText(getActivity(), msg, Toast.LENGTH_LONG).show(); } } } } catch (IOException e) { Log.e("TAG", "MainActivity: IOException", e); } finally { try { reader.close(); } catch (IOException e) { // NOOP } } } }); } else { mWebView.loadUrl( "javascript:" + javascript); } } @Override public void onProgressUpdate(String value) { if (!isAdded()) { return; } String javascript = "img_replace_by_url('" + value + "')"; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { mWebView.evaluateJavascript(javascript, new ValueCallback<String>(){ @Override public void onReceiveValue(String s) { JsonReader reader = new JsonReader(new StringReader(s)); // Must set lenient to parse single values reader.setLenient(true); try { if(reader.peek() != JsonToken.NULL) { if(reader.peek() == JsonToken.STRING) { String msg = reader.nextString(); if(msg != null) { // Toast.makeText(getActivity(), msg, Toast.LENGTH_LONG).show(); } } } } catch (IOException e) { Log.e("TAG", "MainActivity: IOException", e); } finally { try { reader.close(); } catch (IOException e) { // NOOP } } } }); } else { mWebView.loadUrl("javascript:" + javascript); } } @Override public void onFail(Exception e) { e.printStackTrace(); } }).executeOnExecutor(MyAsyncTask.DOWNLOAD_THREAD_POOL_EXECUTOR, urlStrArray); } } }; public static class JavaScriptObject { private Activity mInstance; public JavaScriptObject(Activity instance) { mInstance = instance; } @JavascriptInterface public void openImage(String url) { if (mInstance != null && !mInstance.isFinishing()) { Intent intent = new Intent(mInstance, NewsDetailImageActivity.class); intent.putExtra("imageUrl", url); mInstance.startActivity(intent); } } } /** * WebView正文加载成功之后的回调接口 * */ public interface OnContentLoadListener { public void onComplete(NewsDetailEntity newsDetailEntity); } @Override public void onProgressUpdate(String value) { } @Override protected void onRestoreState(Bundle savedInstanceState) { mNewsId = savedInstanceState.getLong("id"); } @Override protected void onSaveState(Bundle outState) { outState.putLong("id", mNewsId); } @Override protected void onFirstTimeLaunched() { Bundle bundle = getArguments(); mNewsId = bundle != null ? bundle.getLong("id") : 0; } }